package IceAssembly;

import java.io.BufferedWriter;
import java.io.FileWriter;
import java.io.IOException;
import java.io.*;

/**
 * Tables that hold valuable information representing the interesting parts of the 
 * source program for assembling it and generating intermediate output for assembling
 * the program. 
 */
public class Table{
    
    static public String[][] symbol_table = new String[50][4];
    static public int symbol_row = 0;
    
    static public String[][] error_table = new String[3072][2];
    static public int error_row = 0;
    
    static public String[][] intermediate_file = new String[1023][8];
    static public int intermediate_row = 0;
    static public int start_index=0;   
    
    static public String[][] text_records = new String[1023][8];
    static public int text_records_row = 0;
    
    static public File link_error_file = new File("out//error_file.txt");
    static public File link_inter_file = new File("out//inter_file.txt");
    static public File link_symbol_file = new File("out//symbol_file.txt");
    
    
    
    /**
     * Table for symbols defined in the program as labels, and other pseudo-ops. 
     * General structure of the Symbol Table is: symbol name, location counter, 
     * symbol usage, and substitution string.
     * <p>
     * <b>Changelog:</b><br> 
     * 4/13/08 - Initial code - Peter Dietz 
     * 4/18/08 - changed symbol table to a global variable (public) - Keith Hill
     * 4/19/08 - changed parameter relocation from char to string - Peter Dietz
     * <p><b>Error Conditions Tested:</b><br>
     * None yet
     * <b>Date of module being installed:</b> 4/13/08
     * <p><b>Original Author:</b>  Peter Dietz
     * @param  symbol name to be entered into the table
     * @param  usage how it was declared in the program
     * @param  equvalue substitution string of the variable
     * @return 1 if successfully added to table, 0 if it failed to add to table
     */    
    public int addToSymbolTable (String symbol, String usage, String equvalue) {
        Convert convert_instance = new Convert();
        int status;
        if (symbol_row < symbol_table.length) {
            symbol_table[symbol_row][0] = symbol;
            symbol_table[symbol_row][1] = convert_instance.intToFourHex(Control.location_counter);
            symbol_table[symbol_row][2] = usage.toUpperCase();
            try{
                symbol_table[symbol_row][3] = convert_instance.intToFourHex(Integer.valueOf(equvalue));
            }catch(Exception e){
                symbol_table[symbol_row][3] = equvalue;
            }
            symbol_row++;
            status = 1;
            sortSymbolTable();
            addToSymbolFile();
        } else {
            status =  0;
        }       
        return status;
    }

    /**
     * Error table will have all the errors, and warnings from each line so 
     * they may be printed out when it comes time to print out the errors from
     * each line.
     * <p>
     * <b>Changelog:</b><br>
     * 4/13/08 - Initial code - Peter Dietz 
     * 4/14/08 - changed return from void to int - Peter
     * 4/23/08 - line_num usage was changed to global variable - Peter
     * <p><b>Error Conditions Tested:</b>
     * none yet
     * <b>Date of module being installed:</b> 4/13/08
     * <p><b>Original Author:</b>  Peter Dietz
     * @param  errorcode Error number that is defined in iniErrorTable
     * @return 1 if successfully added to error table, other 0 for failure
     */    
    public int addToErrorTable (String errorcode) {
        
        int status;
        if (error_row < error_table.length) {
            error_table[error_row][0] = String.valueOf(Control.line_num+1);
            error_table[error_row][1] = errorcode;
            error_row++;
            status = 1;
            addToErrorFile();
        } else {
            status =  0;
        }    
        Initialize initialize_instance = new Initialize();
        String[] error_message = initialize_instance.getErrorMessage(errorcode);
        System.out.println("");
        System.out.println("On line "+(Control.line_num+1));
        System.out.println("ERROR("+errorcode+"):"+error_message[0]);
        System.out.println("SUGGESTED FIX:"+error_message[1]);
        return status;
    }

    /**
     * The machine code summary of each memory consuming instruction from the ICE
     * source code that will be used to generate the intermediate file. 
     * file. 
     * <ul><b>Intermediate File structure:</b></ul>
     * <li>(0) location counter in hex</li><li>(1) the debug status as a word</li>
     * <li>(2) the object code of instruction mnemonic and function in hex</li>
     * <li>(3) the operand value in binary if possible</li><li>(4) relocation 
     * type consisting of a/r/e/c and it might have multiple if it hasn't been determined yet</li></ul>
     * <p>
     * <b>Changelog:</b> 
     * 4/13/08 - Initial code - Peter Dietz 
     * 4/14/08 - implemented, and changed return from void to int - Peter
     * 4/17/08 - added additional parameter relocation_type - Peter
     * 4/23/08 - location counter was changed to global variable - Peter<br>
     * 5/13/08 - linenum column added in inter_file_array - needed by sp2 - Yogesh Jindal.<br>
     * <p><b>Error Conditions Tested:</b>
     * none yet 
     * <b>Date of module being installed:</b> 4/14/08
     * <p><b>Original Author:</b>  Peter Dietz
     * @param  opfunc_code Code for operation-function pair in 5 bits binary
     * @param  operand Parameter as literal or variable. Literals should be in binary format.
     * @param  relocation_type character signifying the location in program
     * @return 1 if successfully added to intermediate file, else 0 if failure
     */    
    public int addToIntermediateFile (String opfunc_code, String operand, String relocation_type) {
        Convert convert_instance = new Convert();
        if(Control.location_counter==1024){
            return 0;
        }
        int status;
        if (intermediate_row < intermediate_file.length) {
            intermediate_file[intermediate_row][0] = convert_instance.intToFourHex(Control.location_counter);
            intermediate_file[intermediate_row][1] = Control.debug_status;
            intermediate_file[intermediate_row][2] = opfunc_code;
            intermediate_file[intermediate_row][3] = operand;
            intermediate_file[intermediate_row][4] = relocation_type;
            intermediate_file[intermediate_row][5] = String.valueOf(Control.line_num);
            intermediate_row++;
            status = 1;
            addToInterFile();            
        } else {
            status =  0;
        }                
        return status;
    }
    
    /**
     * All the text records used in our program. Each has been finished with pass 
     * two of the assembler. 
     * <ul><b>Text Record structure:</b></ul>
     * <li>(0) Address in hex hhhh</li>
     * <li>(1) the debug status as a word in caps</li>
     * <li>(2) code for instruction or data</li>
     * <li>(3) relocation types for S1/S2 consisting of a r e c. Multiple if not sure</li>
     * <li>(4) S1 operator + or - or blank</li>
     * <li>(5) S1 external reference symbol name</li>
     * <li>(6) S2 operator + or - or blank</li>
     * <li>(7) S2 external reference symbol name</li>
     * </ul>
     * <p>
     * <b>Changelog:</b> 
     * 5/11/08 - Initial code - Peter Dietz 
     * <p><b>Error Conditions Tested:</b>
     * none yet 
     * <b>Date of module being installed:</b> 5/11/08
     * <p><b>Original Author:</b>  Peter Dietz
     * @param  code_data_word 4hex representing instruction code or data word
     * @param  relocation_type character signifying the location in program
     * @param s1_operator if S1 external symbol, then + or -, otherwise blank
     * @param s1_symbol S1 external reference symbol name
     * @param s2_operator if S2 external symbol, then + or -, otherwise blank
     * @param s2_symbol S2 external reference symbol name
     * @return 1 if successfully added to text record, else 0 if failure
     */    
    public int addToTextRecord (String location, String debug, String code_data_word, String relocation_type, String s1_operator, String s1_symbol, String s2_operator, String s2_symbol) {
        Convert convert_instance = new Convert();
        if(Control.location_counter==1024){
            return 0;
        }
        int status;
        if (text_records_row < text_records.length) {
            text_records[text_records_row][0] = convert_instance.intToFourHex(Integer.parseInt(location,16));
            text_records[text_records_row][1] = debug;
            text_records[text_records_row][2] = code_data_word;
            text_records[text_records_row][3] = relocation_type;
            text_records[text_records_row][4] = s1_operator;
            text_records[text_records_row][5] = s1_symbol;
            text_records[text_records_row][6] = s2_operator;
            text_records[text_records_row][7] = s2_symbol;
            text_records_row++;
            status = 1;
        } else {
            status =  0;
        }                
        return status;
    }
    
    /**
     * Performs a lookup in the symbol table for all labels defined acording to
     * the usage <i>usage</i>. If usages were found, then all label names defined
     * as <i>usage</i> will be passed back. <br>
     * <b>Change Log:</b><Br>
     * 4/23/08 - Initial Code - Yogesh Jindal
     * 4/26/08 - Documentation Revised - Peter Dietz
     * 5/9/08 - Changed type to public - needed by other package - Yogesh Jindal.<br>
     * <b>Error Conditions Tested</b>
     * none yet
     * <b>Date of module being installed:</b> 4/23/08
     * <p><b>Original Author:</b>  Yogesh Jindal
     * @param usage how the labels usage is defined in the symbol table
     * @return A string array. Position 0 is a bit flag, 0 means that no such usage
     * type was present in the table, and further positions in the array should be 
     * ignored. If position 0 has a value 1, then the usage type was present in the 
     * table. Further positions in the array will contain label names whose usage was
     * defined as specified as <b>usage</b>
     */
    public String[] getAllLabels(String usage){
        int num_label=0;
        for(int index=0; index<symbol_row; index++){
            if(symbol_table[index][2].compareTo(usage.toUpperCase())==0){
                num_label++;
            }
        }
        String[] label_string = new String[num_label+1];
        if(num_label==0){
            label_string = new String[2];
            label_string[0] = "0"; label_string[1]="";
        }else{
            label_string[0] = "1"; 
        }
        num_label=1;
        for(int index=0; index<symbol_row; index++){
            if(symbol_table[index][2].compareTo(usage.toUpperCase())==0){
                label_string[num_label]=symbol_table[index][0];
                num_label++;
            }
        }
        return label_string;
    }
    
    /**
    * Creates a text file for the intermediate file and dumps the current contents 
    * of the intermediate table to the file.
    * <p>
    * <b>Change Log:</b><br>
    * 4/24/08 - Initial Code - Yogesh Jindal<br>
    * 4/26/08 - Documentation Revised - Peter Dietz<br>
    * <p>
    * <b>Error Conditions Tested:</b><br>
    * none yet<br>
    * <b>Date of module being installed:</b> 4/24/08
    * <p><b>Original Author:</b>  Yogesh Jindal
    */
    public void addToInterFile(){
        try{
            link_inter_file.createNewFile();
            BufferedWriter bw = new BufferedWriter(new FileWriter(link_inter_file));
            for(int i=0; i<intermediate_row; i++){
                bw.write(intermediate_file[i][0]+"\t"+intermediate_file[i][1]+"\t"+intermediate_file[i][2]+"\t"+intermediate_file[i][3]+"\t"+intermediate_file[i][4]);
                bw.write("\n");
            }
            bw.close();
        }catch(IOException e){
            System.out.println("Writing to error file failed");
        }
    }
            
    /**
        * Creates a text file for the error file and dumps the current contents 
        * of the error table to the file.
        * <p>
        * <b>Change Log:</b><br>
        * 4/24/08 - Initial Code - Yogesh Jindal<br>
        * 4/26/08 - Documentation Revised - Peter Dietz<br>
        * <p>
        * <b>Error Conditions Tested:</b><br>
        * INTERNAL: Writing to error file failed<br>
        * <b>Date of module being installed:</b> 4/24/08
        * <p><b>Original Author:</b>  Yogesh Jindal
        */
    public void addToErrorFile(){ 
        //System.out.println(link_error_file.getAbsolutePath());
        try{
            link_error_file.createNewFile();
            BufferedWriter bw = new BufferedWriter(new FileWriter(link_error_file));
            for(int i=0; i<error_row; i++){                
                bw.write(error_table[i][0]+"\t"+error_table[i][1]+"\t");//+initialize_instance.getStringForCode(error_table[i][1]));
                bw.write("\n");
            }
            bw.close();
        }catch(IOException e){
            System.out.println("Writing to error file failed");
        }
        
    }
    
    /**
        * Creates a text file for the symbol file and dumps the current contents 
        * of the symbol table to the file.
        * <p>
        * <b>Change Log:</b><br>
        * 4/24/08 - Initial Code - Yogesh Jindal<br>
        * 4/26/08 - Documentation Revised - Peter Dietz<br>
        * <p>
        * <b>Error Conditions Tested:</b><br>
        * INTERNAL: Writing to error file failed<br>
        * <b>Date of module being installed:</b> 4/24/08
        * <p><b>Original Author:</b>  Yogesh Jindal
        */
    public void addToSymbolFile(){        
        try{
            link_symbol_file.createNewFile();
            BufferedWriter bw = new BufferedWriter(new FileWriter(link_symbol_file));
            for(int i=0; i<symbol_row; i++){                
                bw.write(symbol_table[i][0]+"\t"+symbol_table[i][1]+"\t"+symbol_table[i][2]+"\t"+symbol_table[i][3]);
                bw.write("\n");
            }
            bw.close();
        }catch(IOException e){
            System.out.println("Writing to error file failed");
        }
        
    }
    
     /**
    * The purpose of this function is to find the location counter of a desired label.  
      * The function will search through the current symbol table until a match is found and will return the location of that match.
    * <p>
    * <b>Change Log:</b><br>
    * 5/4/08 - Initial Code Entry - Yogesh Jindal<br>
      * 5/5/08 - Inserted Documentation - Keith Hill <br>
      * 5/9/08 - Changed type to public - needed by other package - Yogesh Jindal.<br>
    * <p>
    * <b>Error Conditions Tested:</b><br>
    * None <br>
    * <p>
    * <b>Date of module being installed:</b> 5/4/08
    * <p><b>Original Author:</b>  <original author>
    * @param label This is the label that the caller needs the location of.
    * @return The function will return the location of label or -100 if the label is not found.
    */
    public String getLcOfLabel(String label){
        for(int index=0; index<symbol_row; index++){
            if(symbol_table[index][0].compareTo(label)==0 && symbol_table[index][2].compareTo("EQU")==0){
                return symbol_table[index][3];
            }
        }        
        for(int index=0; index<symbol_row; index++){
            if(symbol_table[index][0].compareTo(label)==0 && !symbol_table[index][2].contentEquals("SHVAR") && !symbol_table[index][2].contentEquals("EXVAR")){
                return symbol_table[index][1];
            }
        }
        return "-100";
    }
    
    /**
    * This function is passed a string that is currently in the symbol table and will return the equated value of that label.  
     * <p>
    * <b>Change Log:</b><br>
    * 5/4/08 - Initial Code Entry - Yogesh Jindal <br>
      * 5/5/08 - Inserted Documentation - Keith Hill <br>
     * 5/9/08 - Changed type to public. Needed by Other package. - Yogesh Jindal.<br>
    * <p>
    * <b>Error Conditions Tested:</b><br>
    * None <br>
    * <p>
    * <b>Date of module being installed:</b> 5/4/08
    * <p><b>Original Author:</b>  <original author>
    * @param label This is the label that the caller needs the value of.
    * @return The function will return the value of the label or -100 if the label is not found.
    */
    public String getEquValueOfLabel(String label){
        for(int index=0; index<symbol_row; index++){
            if(symbol_table[index][0].compareTo(label)==0 && symbol_table[index][2].contentEquals("EQU")){
                return symbol_table[index][3];
            }
        }
        return "-100";
    }
    
    /**
* This function will be called before the second pass begins.  It sorts the symbol table in alphabetical order. 
     * It uses the findMax helper function several times to determine which item should go next.  It then over writes the 
     * current symbol table. 
* <p>
* <b>Change Log:</b><br>
* 5/3/08 - Initial Code Entry - Keith Hill<br>
* <p>
* <b>Error Conditions Tested:</b><br>
*    None
* <p>
* <b>Date of module being installed:</b> 5/3/08
* <p><b>Original Author:</b>  Keith Hill
*/
    public void sortSymbolTable(){
        if(symbol_row==0){
            return;
        }
        String [][] temp = new String [50][4];
        int counter = 0;
        while(counter<50){
            int max = findMax(symbol_table);
            temp[counter][0] = symbol_table[max][0];
            temp[counter][1] = symbol_table[max][1];
            temp[counter][2] = symbol_table[max][2];
            temp[counter][3] = symbol_table[max][3];
            
            if(symbol_table[max][0] != null){
                symbol_table[max][0] = "";
            }
            counter ++;
        }        
        counter = 0;
        while(counter<50){
            if(!temp[counter][0].equals("")){
                symbol_table[counter][0] = temp[counter][0];
                symbol_table[counter][1] = temp[counter][1];
                symbol_table[counter][2] = temp[counter][2];
                symbol_table[counter][3] = temp[counter][3];
            }
            counter++;
        }
    }
    
    /**
* This function is a helper function for the sortSymbolTable function.  It will determine the next logical choice 
     * and return the location in the array to the function.
* <p>
* <b>Change Log:</b><br>
* 5/3/08 - Initial Code Entry - Keith Hill<br>
* <p>
* <b>Error Conditions Tested:</b><br>
*    None
* <p>
* <b>Date of module being installed:</b> 5/3/08
* <p><b>Original Author:</b>  Keith Hill
     * @param symbols This paramater is the current list of symbols that is passed from the caller.
     * @return The location in the array of the next item is returned.
*/
    public int findMax(String[][] symbols){
        int maxloc = 0, counter = 0;
        String max = "";
        while(counter<50){
            if(symbols[counter][0] != null){
                if(!symbols[counter][0].contentEquals("") ){
                    maxloc = counter;               
                    break;
                }
            }
            counter ++;
        }
        max = symbols[maxloc][0];        
        counter = 0;
        while(counter<50){            
            if(symbols[counter][0] != null){                
                if(symbols[counter][0].toUpperCase().compareTo(max.toUpperCase()) < 0 && !symbols[counter][0].contentEquals("")){
                    max = symbols[counter][0];
                    maxloc = counter;
                }
            }
            counter ++;
        }        
        return maxloc;
    }
    
    /**
     * This function checks if the parameter string is a start label or not.
     * <p>
     * <b>Change Log:</b><br>
     * 5/14/08 - Initial Code Entry - Aashish kumar.<br>
     * <p>
     * <b>Error Conditions Tested:</b><br>
     * None <br>
     * <p>
     * <b>Date of module being installed:</b> 5/4/08
     * <p><b>Original Author:</b>  <original author>
     * @param label - any string.
     * @return - Returns 1 if it is a start label else 0.
     */
    public int isStartLabel(String label){
        String[] all_label = getAllLabels("START");
        if(all_label[1].contentEquals(label)){
            return 1;
        }
        return 0;
    }
}